package p.ithorns.framework.common.utils;

import com.github.rholder.retry.*;

import java.util.concurrent.TimeUnit;
import java.util.function.Predicate;

/**
 * RetryUtil
 *
 * 重试工具
 *
 * @author liuhaitao
 * @version 1.0
 * @date 2021/11/16 17:00
 */
@SuppressWarnings("UnstableApiUsage")
public class RetryUtil {

    /**
     * 重试次数
     */
    private final static int ATTEMPT = 3;

    /**
     * 重试等待时间
     */
    private final static int TIME_WAIT = 5;

    /**
     * 重试时间单位
     */
    private final static TimeUnit TIME_UNIT = TimeUnit.SECONDS;


    public static <V> Retryer<V> create() {
        return create(null, null);
    }

    /**
     * 条件重试
     */
    public static <V> Retryer<V> create(Predicate<V> predicate) {
        return create(predicate, null);
    }

    /**
     * 异常重试
     */
    public static <V> Retryer<V> create(Class<? extends Exception> exClazz) {
        return create(null, exClazz);
    }

    /**
     * 条件和异常重试
     */
    public static <V> Retryer<V> create(Predicate<V> predicate, Class<? extends Exception> exClazz) {
        return create(predicate, exClazz, TIME_WAIT, TIME_UNIT, ATTEMPT, null);
    }

    /**
     * Base构建
     *
     * @param predicate -条件
     * @param exClazz   -异常
     * @param timeWait  -重试等待
     * @param timeUnit  -时间单位
     * @param attempt   -重试次数
     * @param listener  -重试监听
     * @param <V>       -数据返回类型
     * @return Retryer<V> -重试器
     */
    public static <V> Retryer<V> create(Predicate<V> predicate,
                                        Class<? extends Exception> exClazz,
                                        int timeWait,
                                        TimeUnit timeUnit,
                                        int attempt,
                                        RetryListener listener) {
        RetryerBuilder<V> builder = RetryerBuilder.<V>newBuilder()
                // 重调策略：固定等待时间
                .withWaitStrategy(WaitStrategies.fixedWait(timeWait, timeUnit))
                // 重调策略：等待时间递增
                // .withWaitStrategy(WaitStrategies.incrementingWait(1, TimeUnit.SECONDS, 1, TimeUnit.SECONDS))
                // 尝试次数
                .withStopStrategy(StopStrategies.stopAfterAttempt(attempt));

        if (null != predicate) {
            // 条件重试
            builder.retryIfResult(predicate::test);
        }

        if (null != exClazz) {
            // 特定异常时才重试
            builder.retryIfExceptionOfType(exClazz);
        } else {
            // 抛出runtime异常、checked异常时都会重试，可是抛出error不会重试。
            builder.retryIfException();

            // 只会在抛 runtime 异常的时候才重试，checked 异常和 error 都不重试。
            //.retryIfRuntimeException()
        }

        if (null != listener) {
            builder.withRetryListener(listener);
        }

        return builder.build();
    }

}